﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using PlaylistEditorForCentrafuse.GUI;

namespace PlaylistEditorForCentrafuse.Playlist
{
    public enum PlaylistTypeEnum
    {
        Unknown,
        M3U,
        MPL
    }

    public class PlaylistFile
    {
        private PlaylistTypeEnum _type = PlaylistTypeEnum.Unknown;
        private string _filename = "";
		private string _localPath = "";
		private string _remotePath = "";
        private int _missingCount = 0;
        private int _duplicateCount = 0;
        private bool _changed = true;
        private List<PlaylistEntry> _entries = new List<PlaylistEntry>();
        private ListViewItem _listItem = null;

        public bool UseCF3StyleMPL { get; set; }

        public PlaylistFile(bool i_playlistStyle)
        {
            UseCF3StyleMPL = i_playlistStyle;
        }

        public PlaylistFile(string i_filename, PlaylistTypeEnum i_type, bool i_playlistStyle)
        {
            _filename = i_filename;
            _type = i_type;
            UseCF3StyleMPL = i_playlistStyle;
        }

        /////////////////////////////////////////
        // Public Methods
        /////////////////////////////////////////
        #region Public Methods
        public void CalcProperties(List<PlaylistAudioFile> i_audioFiles)
        {
            // Auf Duplikate und Fehlende Dateien prüfen
            _missingCount = 0;
            _duplicateCount = 0;

            if (i_audioFiles.Count > 0)
            {
                foreach (PlaylistEntry entry in _entries)
                {
                    // Audiodatei suchen
                    entry.Audiofile = FindAudiofile(entry.Filename, i_audioFiles);

                    // // Fehlende Datei ?
                    if (entry.Audiofile == null)
                    {
                        ++_missingCount;
                    }
                    else
                    {
                        entry.Audiofile.AddPlaylist(this);
                    }
                }// foreach (PlaylistEntry entry in _entries)

                // Auf Duplikate prüfen
            }// if (i_audioFiles.Count > 0)
            else
            {
                _missingCount = _entries.Count;
            }

            // Anzeige Aktualisieren
            UpdateListView();
        }

        public void Sort()
        {
            _entries.Sort();
            
            _changed = true;
            UpdateListView();
        }

        public void SetPaths(string i_remote, string i_local)
        {
			_localPath = i_local;
			_remotePath = i_remote;

			ReplacePaths(i_remote, i_local);
        }

        public bool ParsePlaylist(string i_filename)
        {
            bool retVal = false;

            // Playlist Typ
            string extension = Path.GetExtension(i_filename).ToLower();
            PlaylistTypeEnum type;

            if (extension == ".m3u")
            {
                type = PlaylistTypeEnum.M3U;
            }
            else if (extension == ".mpl")
            {
                type = PlaylistTypeEnum.MPL;
            }
            else
            {
                type = PlaylistTypeEnum.Unknown;
            }

            // Parsen
            if (type != PlaylistTypeEnum.Unknown)
            {
                StreamReader reader = OpenReadStream(i_filename, type);
                if (reader != null)
                {
                    Filename = i_filename;
                    Type = type;

                    try
                    {
                        // Parse
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            switch (type)
                            {
                                case PlaylistTypeEnum.M3U:
                                    ParseM3uLine(line);
                                    break;
                                case PlaylistTypeEnum.MPL:
                                    ParseMplLine(line);
                                    break;
                            }// switch(type)
                        }// while ((line = reader.ReadLine()) != null)

                        retVal = true;
                        _changed = false;
                    }// try
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message, "Fehler beim Lesen der Datei",
                                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    finally
                    {
                        reader.Close();
                    }
                }// if (reader)
            }// if (type != PlaylistType.Unknown)

            return retVal;
        }

        public bool WritePlaylist()
        {
            bool retVal = false;

            StreamWriter writer = OpenWriteStream(Filename, Type);

            if (writer != null)
            {
				// Pfade ersetzen
				ReplacePaths(_localPath, _remotePath);

				if (Type == PlaylistTypeEnum.MPL && UseCF3StyleMPL)
				{
					// Nur LF schreiben
					writer.NewLine = "\n";
				}

                try
                {
                    foreach (PlaylistEntry entry in _entries)
                    {
                        switch (Type)
                        {
                            case PlaylistTypeEnum.M3U:
                                WriteM3uEntry(entry, writer);
                                break;
                            case PlaylistTypeEnum.MPL:
                                WriteMplEntry(entry, writer);
                                break;
                        }// switch (i_type)
                    }
                    retVal = true;
                    _changed = false;
                    UpdateListView();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message, "Fehler beim Schreiben der Datei",
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    writer.Close();
					// Pfade wieder rückgängig
					ReplacePaths(_remotePath, _localPath);
                }
            }

            return retVal;
        }

        public void AddAudioFile(PlaylistAudioFile i_file, bool i_duplicate)
        {
            i_file.LoadTags(true);

            // Audiodatei aktualisieren
            i_file.AddPlaylist(this);

            PlaylistEntry entry;

            if (i_file.ID3v2 != null)
            {
                entry = new PlaylistEntry(this, i_file, i_file.FileName, i_file.ID3v2.Title,
                                                        i_file.ID3v2.Artist, i_file.ID3v2.Album, i_file.ID3v2.Genre,
                                                        i_file.ID3v2.Track, i_file.ID3v2.Year, DateTime.Now.ToString(),
                                                        DateTime.Now.ToString(), "0", "0");
            }
            else
            {
                entry = new PlaylistEntry(this, i_file, i_file.FileName, i_file.GetTitleFromFilename(),
                                          i_file.GetArtistFromFilename(), "", "", "", "", 
                                          DateTime.Now.ToString(), DateTime.Now.ToString(), "0", "0");
            }

            if (i_duplicate)
            {
                ++_duplicateCount;
                entry.IsDuplicate = true;
            }
            _entries.Add(entry);

            _changed = true;
            UpdateListView();
        }

        public void RemoveEntry(PlaylistEntry i_entry)
        {
            if (_entries.Remove(i_entry))
            {
                _changed = true;
                
                if (i_entry.IsMissing)
                {
                    --_missingCount;
                }
                if (i_entry.IsDuplicate)
                {
                    --_duplicateCount;
                }
                
                UpdateListView();
            }
        }
        #endregion

        /////////////////////////////////////////
        // Private Methods
        /////////////////////////////////////////
        #region Private Methods
        private PlaylistAudioFile FindAudiofile(string i_filename, List<PlaylistAudioFile> i_audioFiles)
        {
            foreach (PlaylistAudioFile file in i_audioFiles)
            {
                if (file.FileName == i_filename)
                {
                    return file;
                }
            }

            return null;
        }

        private StreamWriter OpenWriteStream(string i_filename, PlaylistTypeEnum i_type)
        {
            StreamWriter retVal = null;

            // Datei erstellen
            try
            {
                // Richtiges Encoding
                switch (i_type)
                {
                    case PlaylistTypeEnum.M3U:
                        retVal = new StreamWriter(i_filename, false, Encoding.ASCII);
                        break;
                    case PlaylistTypeEnum.MPL:
                        retVal = new StreamWriter(i_filename, false, Encoding.Unicode);
                        break;
                }// switch (i_type)
            }// try

            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Fehler beim Öffnen der Datei\n[" + i_filename + "] !",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);

                retVal = null;
            }

            return retVal;
        }

        private StreamReader OpenReadStream(string i_filename, PlaylistTypeEnum i_type)
        {
            StreamReader retVal = null;

            // Datei öffnen
            try
            {
                // Richtiges Encoding
                switch (i_type)
                {
                    case PlaylistTypeEnum.M3U:
                        retVal = new StreamReader(i_filename, Encoding.ASCII);
                        break;
                    case PlaylistTypeEnum.MPL:
                        retVal = new StreamReader(i_filename, Encoding.Unicode);
                        break;
                }// switch (i_type)
            }// try

            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Fehler beim Öffnen der Datei [" + i_filename + "]",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);

                retVal = null;
            }

            return retVal;
        }

        private void ParseMplLine(string i_line)
        {
            if (i_line.Length > 0)
            {
                char[] separator = new char[1] { '|' };
                string[] splits = i_line.Split(separator);

                if (splits.Length == 11)
                {
                    // CF3 Style
                    _entries.Add(new PlaylistEntry(this, null, 
                                                   splits[10], splits[3], splits[1], splits[0], 
                                                   splits[2], splits[4], splits[5], splits[6], 
                                                   splits[7], splits[8], splits[9]));
                }
                else if (splits.Length == 2)
                {
                    // CF2 Style
                    _entries.Add(new PlaylistEntry(this, null, 
                                                   splits[1], splits[0], "", "", "", "", "", 
                                                   DateTime.Now.ToString(), DateTime.Now.ToString(), "0", "0"));
                }
            }// if (line.Length > 0)
        }

        private void ParseM3uLine(string i_line)
        {
            if (i_line.Length > 0 && i_line.StartsWith("#EXT") == false)
            {
                _entries.Add(new PlaylistEntry(this, null, i_line, "", "", "", "", "", "", 
                                               DateTime.Now.ToString(), DateTime.Now.ToString(), "0", "0"));
            }// if (line.Length > 0)
        }

        private void WriteMplEntry(PlaylistEntry i_entry, StreamWriter i_writer)
        {
            if (UseCF3StyleMPL)
            {
                int songnr;
                try
                {
                    songnr = int.Parse(i_entry.SongNr);
                }
                catch (Exception)
                {
                    songnr = 0;
                }

                StringBuilder line = new StringBuilder();
                line.Append(i_entry.Album)
                .Append("|")
                .Append(i_entry.Interpret)
                .Append("|")
                .Append(i_entry.Genre)
                .Append("|")
                .Append(i_entry.Title)
                .Append("|")
                .Append(songnr)
                .Append("|")
                .Append(i_entry.Year)
                .Append("|")
                .Append(i_entry.Date1)
                .Append("|")
                .Append(i_entry.Date2)
                .Append("|")
                .Append(i_entry.Empty1)
                .Append("|")
                .Append(i_entry.Empty2)
                .Append("|")
                .Append(i_entry.Filename);

                i_writer.WriteLine(line.ToString());
            }
            else
            {
                i_writer.WriteLine(i_entry.Title + "|" + i_entry.Filename);
            }
        }

        private void WriteM3uEntry(PlaylistEntry i_entry, StreamWriter i_writer)
        {
            i_writer.WriteLine(i_entry.Filename);
        }

        private void UpdateListView()
        {
            if (_listItem != null)
            {
                 // Geändert -> Fett
                _listItem.Font = new Font(_listItem.Font, _changed ? FontStyle.Bold : FontStyle.Regular);
                
                _listItem.Text = Path.GetFileName(_filename);
                _listItem.SubItems[(int)frmEditor.ColumnsPlaylistfile.NoOfSongs].Text = 
                    _entries.Count.ToString();
                _listItem.SubItems[(int)frmEditor.ColumnsPlaylistfile.MissingCount].Text = 
                    _missingCount.ToString();
                _listItem.SubItems[(int)frmEditor.ColumnsPlaylistfile.DuplicateCount].Text = 
                    _duplicateCount.ToString();
                _listItem.SubItems[(int)frmEditor.ColumnsPlaylistfile.Type].Text = _type.ToString();

                // Gruppe
                if (_missingCount > 0)
                {
                    _listItem.ForeColor = Color.Red;
                }
                else if (_duplicateCount > 0)
                {
                    _listItem.ForeColor = Color.Orange;
                }
                else
                {
                    _listItem.ForeColor = Color.Green;
                }
            }// if (_listItem != null)
        }

		private void ReplacePaths(string i_search, string i_replace)
		{
			if (!string.IsNullOrEmpty(i_search)
				&& !string.IsNullOrEmpty(i_replace)
				&& i_search != i_replace)
			{
				for (int i = 0; i < _entries.Count; i++)
				{
					if (_entries[i].Filename.Length > 0)
					{
						_entries[i].Filename =
							_entries[i].Filename.
							Replace(i_search, i_replace);
					}// if (i_playlist.PlaylistEntries[i].filename.Length > 0)
				}// for (int i = 0; i < i_playlist.PlaylistEntries.Count; i++)
			}
		}

        #endregion

        /////////////////////////////////////////
        // Getters / Setters
        /////////////////////////////////////////
        #region Getters / Setters
        public List<PlaylistEntry> Entries
        {
            get
            {
                return _entries;
            }
        }

        public int MissingCount
        {
            get
            {
                return _missingCount;
            }
        }

        public int DuplicateCount
        {
            get
            {
                return _duplicateCount;
            }
        }

        public bool Changed
        {
            get
            {
                return _changed;
            }
            set
            {
                if (value)
                {
                    _changed = true;
                    UpdateListView();
                }
            }
        }

        public string Filename
        {
            get
            {
                return _filename;
            }
            set
            {
                _filename = value;
                UpdateListView();
            }
        }

        public PlaylistTypeEnum Type
        {
            get
            {
                return _type;
            }
            set
            {
                _type = value;
                UpdateListView();
            }
        }

        public ListViewItem ListItem
        {
            get
            {
                return _listItem;
            }
            set
            {
                _listItem = value;
                UpdateListView();
            }
        }
        #endregion
    }
}